Header file flag.hpp

namespace type_safe
{
    class flag;
    
    constexpr bool operator==(flag lhs, flag rhs) noexcept;
    template <typename T>
    constexpr bool operator==(flag lhs, T rhs) noexcept;
    template <typename T>
    constexpr bool operator==(T lhs, flag rhs) noexcept;
    
    constexpr bool operator!=(flag lhs, flag rhs) noexcept;
    template <typename T>
    constexpr bool operator!=(flag lhs, T rhs) noexcept;
    template <typename T>
    constexpr bool operator!=(T lhs, flag rhs) noexcept;
}

Class type_safe::flag [types]

class flag
{
public:
    flag() = delete;
    
    template <typename T, typename = detail::enable_boolean<T>>
    constexpr flag(T initial_state) noexcept;
    
    bool toggle() noexcept;
    
    template <typename T>
    void change(T new_state) noexcept;
    
    void set() noexcept;
    
    bool try_set() noexcept;
    
    void reset() noexcept;
    
    bool try_reset() noexcept;
};

A type safe flag, it can either be true or false.

Consider the following snippet:

auto was_newl = false;
for (auto x : …)
{
    if (x == '/n')
    {
         assert(!was_newl); // want to change value here
         was_newl = true;
    }
    else if (was_newl)
    {
        do_sth(c);
        was_newl = false; // separate step, easy to forget (I did here originally!)
    }
}

With flag, it is better:

type_safe::flag was_newl(false);
for (auto x : …)
{
     if (x == '/n')
         was_newl.change(true); // asserts that value is changed
     else if (was_newl.try_reset()) // resets flag and returns whether value changed
         do_sth(x); // no way to forget
}

Notes: It is named flag for consistency with std::atomic_flag, even though this one can provide an extended interface as it is not atomic. flag has nothing to do with ts::flag_set.

Function template type_safe::flag::flag

template <typename T, typename = detail::enable_boolean<T>>
constexpr flag(T initial_state) noexcept;

Effects: Gives the flag the initial state.

Notes: This function does not participate in overload resolution if T is not a boolean type. \param 1 \exclude

Function type_safe::flag::toggle

bool toggle() noexcept;

Effects: Flips the state of the flag.

Returns: The old value.

Function template type_safe::flag::change

template <typename T>
void change(T new_state) noexcept;

Effects: Sets its state to the new one.

Requires: The new state must be different than the old one.

Function type_safe::flag::set

void set() noexcept;

Effects: Sets its state to true.

Function type_safe::flag::try_set

bool try_set() noexcept;

Effects: Sets its state to true.

Returns: true if the previous state was false, false otherwise, i.e. whether or not the state was changed.

Function type_safe::flag::reset

void reset() noexcept;

Effects: Sets its state to false.

Function type_safe::flag::try_reset

bool try_reset() noexcept;

Effects: Sets its state to false.

Returns: true if the previous state was true, false otherwise, i.e. whether or not the state was changed.


Comparison operator type_safe::operator== [types]

(1)  constexpr bool operator==(flag lhs, flag rhs) noexcept;

(2)  template <typename T>
     constexpr bool operator==(flag lhs, T rhs) noexcept;

(3)  template <typename T>
     constexpr bool operator==(T lhs, flag rhs) noexcept;

ts::flag equality comparison.

Returns: true if (1) both ts::flag objects are in the same state, (2)/(3) the flag is in the given state.

Notes: (2)/(3) do not participate in overload resolution unless T is a boolean type.

Comparison operator type_safe::operator!= [types]

(1)  constexpr bool operator!=(flag lhs, flag rhs) noexcept;

(2)  template <typename T>
     constexpr bool operator!=(flag lhs, T rhs) noexcept;

(3)  template <typename T>
     constexpr bool operator!=(T lhs, flag rhs) noexcept;

ts::flag in-equality comparison.

Returns: true if (1) both ts::flag objects are in the same state, (2)/(3) the flag is in the given state.

Notes: (2)/(3) do not participate in overload resolution unless T is a boolean type.